in ggplot
https://stackoverflow.com/questions/33227182/how-to-set-use-ggplot2-to-map-a-raster
library(rnaturalearth)
Support for Spatial objects (`sp`) will be deprecated in {rnaturalearth} and will be removed in a future release of the package. Please use `sf` objects with {rnaturalearth}. For example: `ne_download(returnclass = 'sf')`
Attaching package: ‘rnaturalearth’
The following object is masked from ‘package:rnaturalearthdata’:
countries110
datafold <- "../data/JuneauBathy.tif"
test <- raster(datafold)
test_spdf <- as(test, "SpatialPixelsDataFrame")
test_df <- as.data.frame(test_spdf)
colnames(test_df) <- c("value", "x", "y")
zoom.df <- test_df %>%
filter(x > -134.95, x < -134.76, y > 58.4, y < 58.55)
#filter(x > -135, x < -134, y > 58.2, y < 58.7)
zoom.df.w.gray <- zoom.df %>%
mutate(value = ifelse(value >=0, NA, value)) # set land to NA to shade it gray
# fully zoomed out
full_size_raster <- test_df %>%
mutate(value = ifelse(value >=0, NA, value)) # set land to NA to shade it gray
# minimum value below sea level
mi <- min(zoom.df$value)
# Break points sequence for below sea level
s1 <- seq(from=mi, to=0, by=0 - mi / 50)
depth.scale <- round(s1, 0)
depth.scale <- unique(depth.scale)
depth.scale
[1] -262 -257 -252 -247 -241 -236 -231 -226 -220 -215 -210 -205 -199 -194 -189 -184 -178 -173 -168 -163 -157 -152 -147 -142 -136 -131 -126 -121
[29] -115 -110 -105 -100 -94 -89 -84 -79 -73 -68 -63 -58 -52 -47 -42 -37 -31 -26 -21 -16 -10 -5 0
breaks = levels(depth.scale)[floor(seq(1, nlevels(depth.scale), length.out = 10))]
new.depth.scale <- depth.scale[seq(1, length(depth.scale), 3)]
zoomed.out <- ggplot() +
geom_tile(data=zoom.df.w.gray, aes(x=x, y=y, fill=value), alpha=0.8) +
scale_fill_steps2(low = "navyblue",
mid = "dodgerblue4",
high = "lightsteelblue1",
midpoint = -155,
na.value = "grey50",
breaks = new.depth.scale) +
coord_equal() +
theme(panel.border = element_blank(),
panel.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.text = element_text(size = 12),
axis.title.x = element_text(size = 14, margin = margin(t=10)),
axis.title.y = element_text(size = 14, margin = margin(r=10)),
axis.line = element_line(color = "black"),
legend.position="right") +
theme(legend.text = element_text(size = 8)) +
theme(legend.key.height=unit(1.8, "cm")) +
labs(fill = "Depth (m)",
x = "Longitude (W)",
y = "Latitude (N)")
zoomed.out

#ggsave("pdf_outputs/amalgaBathy.pdf")
That’s more zoomed out, but gives a better perspective…
Then here’s more zoomed in on Amalga:
more.zoom.df <- test_df %>%
filter(x > -134.85, x < -134.77, y > 58.43, y < 58.51) %>%
mutate(value = ifelse(value >=0, NA, value)) # set land to NA to shade it gray
zoomed.map <- ggplot() +
geom_tile(data=more.zoom.df, aes(x=x, y=y, fill=value), alpha=0.8) +
scale_fill_steps2(low = "navyblue",
mid = "dodgerblue4",
high = "lightsteelblue1",
midpoint = -97,
#space = "Lab",
na.value = "grey50",
breaks = new.depth.scale) +
coord_equal() +
coord_equal() +
theme(panel.border = element_blank(),
panel.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.text = element_text(size = 12),
axis.title.x = element_text(size = 14, margin = margin(t=10)),
axis.title.y = element_text(size = 14, margin = margin(r=10)),
axis.line = element_line(color = "black"),
legend.position="right") +
theme(legend.text = element_text(size = 8)) +
theme(legend.key.height=unit(1.8, "cm")) +
labs(fill = "Depth (m)",
x = "Longitude (W)",
y = "Latitude (N)")
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
zoomed.map

#ggsave("amalgaBathyZoom.pdf")
Add the transect line to this map?
# read in the data
# this dataframe is produced in the CTD analysis on the VM
# 08-ctd-cast-data.Rmd
ctd <- read_csv("../data/ctdDataframe.csv")
Rows: 2538 Columns: 16── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): ctd_sample, tide
dbl (13): lat, long, depth_m, salinity, density, pressure_decibar, temp_c, conductivity, sp_conduct, sound_velocity, duration, id, distance
dttm (1): time
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# just one of the transects (not both AM and PM)
am <- ctd %>%
filter(tide == "AM_incoming") %>%
dplyr::select(lat, long, id, distance) %>%
unique() %>%
filter(id != 1)
am %>%
ggplot(aes(x = long, y = lat, label = id)) +
geom_text()

zoomed.map +
geom_point(data = am, aes(x = long, y = lat, label = id), size = 1, color = "firebrick2")
Warning: Ignoring unknown aesthetics: label

#ggsave("pdf_outputs/amalgaTransectBathy.pdf", height = 6, width = 7)
Zoom more
highest.res <- more.zoom.df %>%
filter(x > -134.83, x < -134.779, y > 58.47, y < 58.51) %>%
mutate(value = ifelse(value >=0, NA, value)) # set land to NA to shade it gray
highest.zoom.plot <- ggplot() +
geom_tile(data=highest.res, aes(x=x, y=y, fill = value, color = value)) +
scale_fill_steps2(low = "navyblue",
mid = "dodgerblue4",
high = "lightsteelblue1",
midpoint = -99,
space = "Lab",
na.value = "grey50",
breaks = new.depth.scale) +
scale_color_steps2(low = "navyblue",
mid = "dodgerblue4",
high = "lightsteelblue1",
midpoint = -99,
na.value = "grey50",
breaks = new.depth.scale) +
coord_equal() +
theme(panel.border = element_blank(),
panel.background = element_blank(),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
axis.text = element_text(size = 8),
axis.title.x = element_text(size = 10, margin = margin(t=10)),
axis.title.y = element_text(size = 10, margin = margin(r=10)),
axis.line = element_line(color = "gray50"),
legend.position="right") +
theme(legend.text = element_text(size = 6)) +
# theme(legend.key.height=unit(1, "cm")) +
labs(fill = "Depth (m)",
x = "Longitude (W)",
y = "Latitude (N)") +
guides(color = F) +
scale_y_continuous(expand = c(0,0), breaks = c(58.48, 58.49, 58.50)) +
scale_x_continuous(expand = c(0,0))
highest.zoom.plot

transect2021 <- highest.zoom.plot +
geom_point(data = am, aes(x = long, y = lat), size = 1, color = "red") +
#annotate(geom = "label", x = -134.795, y = 58.504, label = "Amalga Harbor", size = 4) +
#annotate(geom = "label", x = -134.81, y = 58.494, label = "2021 transect", size = 3, fontface = "bold") +
annotate(geom = "label", x = -134.78999, y = 58.496, label = "pens", size = 3, color = "black") +
annotate(geom = "label", x = -134.808, y = 58.492, label = "1000m", size = 3, color = "black") +
annotate(geom = "label", x = -134.825, y = 58.485, label = "2000m", size = 3, color = "black") +
scale_x_continuous(limits = c(-134.83, -134.78), breaks = c(-134.82, -134.80, -134.78)) +
scale_y_continuous(limits = c(58.47, 58.51))
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.Scale for y is already present.
Adding another scale for y, which will replace the existing scale.
#labs(title = "2021 transect") +
#scale_fill_continuous(breaks = c(0, -10, -25, -50, -75, -100, -125, -150, -200))
#ggsave("pdf_outputs/amalgaTransectBathyZoom.pdf", width = 6, height = 5)
at 560 m, it’s basically the edge of Amalga Harbor.
Add the transect to the zoomed out map:
map2021_out <- zoomed.out +
geom_point(data = am, aes(x = long, y = lat), size = 1, color = "black") +
annotate(geom = "text", x = -134.786, y = 58.51, label = "Amalga Harbor", size = 4, color = "white", fontface = "bold") +
annotate(geom = "label", x = -134.83, y = 58.494, label = "2021 transect", size = 3, fontface = "bold")
With these maps, I should be able to combine the two transect years
with cowplot or similar.
For the 2022 data, I need the equivalent from the CTD casts for the
transect.
CTD data from 2022
ctd.2022 <- read_csv("../data/2022ctdDataframe.csv")
Rows: 1360 Columns: 15── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (2): ctd_sample, tide
dbl (12): lat, long, depth_m, salinity, density, pressure_decibar, temp_c, conductivity, sp_conduct, sound_velocity, duration, depth
dttm (1): time
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
ctd.2022
# grab the data for May 5 and one tide for data points for the map
one.set.2022 <- ctd.2022 %>%
filter(str_detect(time, "2022-05-05") &
tide == "AM_outgoing") %>%
dplyr::select(ctd_sample, lat, long) %>%
unique() %>%
filter(!ctd_sample %in% c("163024", "191935", "175337", "185047")) %>%
mutate(Year = "2022")
fp <- am %>%
mutate(Year = "2021") %>%
bind_rows(one.set.2022)
amalga_plot <- highest.zoom.plot +
new_scale_color() +
geom_point(data = fp, aes(x = long, y = lat, shape = Year, color = Year), size = 1.5) +
annotate(geom = "label", x = -134.789, y = 58.496, label = "pens", size = 3, color = "black") +
annotate(geom = "label", x = -134.808, y = 58.4865, label = "1000m", size = 3, color = "black") +
annotate(geom = "label", x = -134.826, y = 58.485, label = "2000m", size = 3, color = "black") +
scale_color_manual(values = c("coral3", "gold")) +
scale_shape_manual(values = c(16,17)) +
theme(
legend.position = "bottom",
legend.spacing.x = unit(0, "cm"),
legend.key = element_rect(fill = "white"),
legend.spacing = unit(3, "cm")
) +
guides(fill = guide_legend(label.position = "bottom",
nrow = 1,
keywidth = unit(0.5, "cm"),
title.hjust = unit(0.2, "cm"),
title.vjust = unit(0.4, "cm")),
color = guide_legend(override.aes = list(size = 5)))
amalga_plot
ggsave("pdf_outputs/amalgaTransectBathyZoom.png", width = 6, height = 5)

# z.o <- zoomed.out +
# geom_point(data = one.set.2022, aes(x = long, y = lat), size = 1, color = "white") +
# annotate(geom = "label", x = -134.8, y = 58.51, label = "Amalga Harbor", size = 4, color = "black") +
# annotate(geom = "label", x = -134.86, y = 58.48, label = "background", size = 3, color = "black") +
# annotate(geom = "label", x = -134.80, y = 58.47, label = "transects", size = 3, color = "black") +
# theme(
# legend.position = "none"
# )
#
#
# z.i.22 <- highest.zoom.plot +
# geom_point(data = one.set.2022, aes(x = long, y = lat), size = 1, color = "red") +
# annotate(geom = "label", x = -134.78999, y = 58.496, label = "pens", size = 3, color = "black") +
# annotate(geom = "label", x = -134.808, y = 58.492, label = "1000m", size = 3, color = "black") +
# annotate(geom = "label", x = -134.825, y = 58.485, label = "2000m", size = 3, color = "black") +
# scale_x_continuous(limits = c(-134.83, -134.78), breaks = c(-134.82, -134.80, -134.78)) +
# scale_y_continuous(limits = c(58.47, 58.51)) +
# theme(
# # legend.position = "none",
# # axis.text.x = element_blank(),
# # axis.title.x = element_blank(),
# # axis.ticks.x = element_blank(),
# axis.title.y = element_blank()
# )
#
# z.i.21 <- highest.zoom.plot +
# geom_point(data = am, aes(x = long, y = lat), size = 1, color = "red") +
# annotate(geom = "label", x = -134.78999, y = 58.496, label = "pens", size = 3, color = "black") +
# annotate(geom = "label", x = -134.808, y = 58.492, label = "1000m", size = 3, color = "black") +
# annotate(geom = "label", x = -134.825, y = 58.485, label = "2000m", size = 3, color = "black") +
# scale_x_continuous(limits = c(-134.83, -134.78), breaks = c(-134.82, -134.80, -134.78)) +
# scale_y_continuous(limits = c(58.47, 58.51)) +
# theme(
# #legend.position = "none",
# axis.text.x = element_blank(),
# axis.title.x = element_blank(),
# axis.ticks.x = element_blank(),
# axis.title.y = element_blank()
# )
library(ggOceanMaps)
ggOceanMaps: Setting data download folder to a temporary folder /var/folders/_5/zm9fzz0d1j139pfm56sqzlfs8qyj5v/T//RtmpQYxlZb. This
means that any downloaded map data need to be downloaded again when you restart R. To avoid this problem, change the default path
to a permanent folder on your computer. Add following lines to your .Rprofile file: {.ggOceanMapsenv <- new.env();
.ggOceanMapsenv$datapath <- 'YourCustomPath'}. You can use usethis::edit_r_profile() to edit the file. '~/ggOceanMapsLargeData'
would make it in a writable folder on most operating systems.
Attaching package: ‘ggOceanMaps’
The following object is masked from ‘package:ggthemes’:
theme_map



Combine the ak plot and the Amalga plot
(ak_plot | interm_p) / amalga_plot2 + plot_layout(nrow = 2, heights = c(2,3)) +
plot_annotation(tag_levels = 'A') &
theme(plot.tag = element_text(size = 8),
plot.margin = margin(0.1, 0.1, 0.1, 0.1, "cm"))
ggsave("pdf_outputs/amalga_combined_maps.png", width = 7, height = 7)

Bathy transect
library(marmap)
# load datasets
# data(nw.atlantic); as.bathy(nw.atlantic) -> atl
# data(nw.atlantic.coast)
#
# # Example 1. get.transect(), without use of locator()
# get.transect(atl, -65, 43,-59,40) -> test ; plot(test[,3]~test[,2],type="l")
# get.transect(atl, -65, 43,-59,40, distance=TRUE) -> test ; plot(test[,4]~test[,3],type="l")
#
# # Example 2. get.transect(), without use of locator(); pretty plot
# par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
# plot(atl, deep=-6000, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
# lines(nw.atlantic.coast)
#
# get.transect(atl, -75, 44,-46,32, loc=FALSE, dis=TRUE) -> test
# points(test$lon,test$lat,type="l",col="blue",lwd=2,lty=2)
# plotProfile(test)
#
# # Example 3. get.transect(), with use of locator(); pretty plot
# ## Not run:
# par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
# plot(atl, deep=-6000, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
# lines(nw.atlantic.coast)
#
# get.transect(atl, loc=TRUE, dis=TRUE, col=2, lty=2) -> test
# plotProfile(test)
## End(Not run)
Very cool, so if I can get my data into that format, I should be able
to do something similar.
# -134.7925, -134.8213, 58.4946, 58.4848
# load dataset
juneau <- getNOAA.bathy(lon1 = -140, lon2 = -130,
lat1 = 60, lat2 = 55, resolution = 1)
plot(juneau)
summary(juneau)
# Creating a custom palette of blues
blues <- c("lightsteelblue4", "lightsteelblue3",
"lightsteelblue2", "lightsteelblue1")
# Plotting the bathymetry with different colors for land and sea
plot(juneau, image = TRUE, land = TRUE, lwd = 0.1,
bpal = list(c(0, max(juneau), "grey"),
c(min(juneau),0,blues)))
# Making the coastline more visible
plot(juneau, deep = 0, shallow = 0, step = 0,
lwd = 0.4, add = TRUE)
zoom out even more to give perspective on location:
juneau.out <- getNOAA.bathy(lon1 = -179, lon2 = -125,
lat1 = 75, lat2 = 48, resolution = 8)
plot(juneau.out, image = TRUE, land = TRUE, lwd = 0.1,
bpal = list(c(0, max(juneau.out), "grey"),
c(min(juneau.out),0,blues)))
# Making the coastline more visible
plot(juneau.out, deep = 0, shallow = 0, step = 0,
lwd = 0.4, add = TRUE)
That doesn’t look as good as the DEM, but hopefully it can give us
the bathy transect.
# Example 1. get.transect(), without use of locator()
get.transect(juneau, -134.8213, 58.4848, -134.7925, 58.4946) -> test ; plot(test[,3]~test[,2],type="l")
get.transect(juneau, -134.8213, 58.4848, -134.7925, 58.4946, distance=TRUE) -> test ; plot(test[,4]~test[,3],type="l")
# Example 2. get.transect(), without use of locator(); pretty plot
par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
plot(juneau, deep=-800, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
#lines(nw.atlantic.coast)
get.transect(juneau, -134.8213, 58.4848, -134.7925, 58.4946, loc=FALSE, dis=TRUE) -> test
points(test$lon,test$lat,type="l",col="blue",lwd=2,lty=2)
plotProfile(test)
# Example 3. get.transect(), with use of locator(); pretty plot
## Not run:
par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
plot(juneau, deep=-800, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
lines(juneau)
get.transect(juneau, loc=TRUE, dis=TRUE, col=2, lty=2) -> test
plotProfile(test)
bathy.df <- highest.res %>%
rename(longitude = x, latitude = y, depth = value)
bathy.df %>%
write_csv("amalgaBathyDf.csv")
read.bathy("amalgaBathyDF.csv", header = T)
# Example 1. get.transect(), without use of locator()
get.transect(highest.res, -134.8213, 58.4848, -134.7925, 58.4946) -> test ; plot(test[,3]~test[,2],type="l")
get.transect(highest.res, -134.8213, 58.4848, -134.7925, 58.4946, distance=TRUE) -> test ; plot(test[,4]~test[,3],type="l")
# Example 2. get.transect(), without use of locator(); pretty plot
par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
plot(test_spdf, deep=-800, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
#lines(nw.atlantic.coast)
get.transect(test_spdf, -134.8213, 58.4848, -134.7925, 58.4946, loc=FALSE, dis=TRUE) -> test
points(test$lon,test$lat,type="l",col="blue",lwd=2,lty=2)
plotProfile(test)
# Example 3. get.transect(), with use of locator(); pretty plot
## Not run:
par(mfrow=c(2,1),mai=c(1.2, 1, 0.1, 0.1))
plot(test_spdf, deep=-800, shallow=-10, step=1000, lwd=0.5, col="grey50",drawlabels=TRUE)
lines(test_spdf)
Also this: https://stackoverflow.com/questions/47047623/projectraster-raster-projection-of-bathymetry-data-noaa-nc-in-the-pacific
LS0tCnRpdGxlOiAiYmF0aHltYXAtZm9yLWFtYWxnYSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKMjQgRmViIDIwMjIKCmBgYHtyIGxvYWQtcGFja2FnZXN9CiMgbGlicmFyeSh0aWR5dmVyc2UpCiMgbGlicmFyeShuY2RmNCkKIyBsaWJyYXJ5KHJhc3RlcikKYGBgCgpgYGB7ciBsb2FkLWRhdGF9CiMgTGV0J3Mgc2VlIGlmIHRoaXMgREVNIGZyb20gTk9BQSBpcyB1c2VmdWwKIyBqdW5lYXUgPC0gcmFzdGVyKCJKdW5lYXVCYXRoeS50aWYiKQoKIyBwbG90KGp1bmVhdSkKYGBgCkZpbHRlciBvdXQgdGhlIGxhbmQgdmFsdWVzIGJ5IHNldHRpbmcgPj0wCgpsZWFuaW5nIG9uIHRoaXM6IGh0dHBzOi8vd3d3LmJlbmphbWluYmVsbC5jby51ay8yMDE5LzA4L2JhdGh5bWV0cmljLW1hcHMtaW4tci1jb2xvdXItcGFsZXR0ZXMuaHRtbAoKVG8gZmlndXJlIG91dCBhIGNvbG9yIHNjaGVtZSwgbmVlZCBtaW4gYW5kIG1heApgYGB7cn0KIyBDYWxjdWxhdGUgbWluIGFuZCBtYXggdmFsdWVzCiMgbWkgPC0gY2VsbFN0YXRzKGp1bmVhdSwgc3RhdD0ibWluIiktMTAwCiMgbWEgPC0gY2VsbFN0YXRzKGp1bmVhdSwgc3RhdD0ibWF4IikrMTAwCmBgYAoKYGBge3J9CiAgICAjICMgQnJlYWsgcG9pbnRzIHNlcXVlbmNlIGZvciBiZWxvdyBzZWEgbGV2ZWwKICAgICMgczEgPC0gc2VxKGZyb209bWksIHRvPTAsIGJ5PTAgLSBtaSAvIDUwKQogICAgIyAjIEJyZWFrIHBvaW50cyBzZXF1ZW5jZSBmb3IgYWJvdmUgc2VhIGxldmVsCiAgICAjIHMyIDwtIHNlcShmcm9tPTAsIHRvPW1hLCBieT1tYSAvIDUwKQogICAgIyAKICAgICMgICAgICMgUm91bmQgc2VxdWVuY2UgdG8gbmVhcmVzdCAxMDAKICAgICMgczEgPC0gcm91bmQoczEsIC0xKQogICAgIyBzMiA8LSByb3VuZChzMiwgLTEpCiAgICAjIAogICAgIyAgICAgIyBPbmx5IHNob3cgdW5pcXVlIG51bWJlcnMKICAgICMgczEgPC0gdW5pcXVlKHMxKQogICAgIyBzMiA8LSB1bmlxdWUoczIpCiAgICAjIAogICAgIyBzMQogICAgIyAKICAgICMgczIKYGBgCmBgYHtyfQogICAgIyBDb21iaW5lIHNlcXVlbmNlcyBhbmQgcmVtb3ZlIHRoZSBmaXJzdCB2YWx1ZSBmcm9tIHNlY29uZCBzZXF1ZW5jZQogICAgIyBzMyA8LSBjKHMxLCBzMlstMV0pCmBgYAoKCmBgYHtyfQogICAgIyBQbG90CiAgICAjIHBsb3QoanVuZWF1LCBjb2w9YyhibHVlLmNvbCg1MCksIHRlcnJhaW4uY29sb3JzKDUwKSksIGJyZWFrcz1zMykKYGBgCgpgYGB7cn0KIyBjcm9wIDwtIGV4dGVudCgtMTM0LjgsLTEzNC42LDU4LjQ1LDU4LjUpCiMgCiMgZXh0ZW50KGp1bmVhdSkKIyAKIyBqdW5lLnpvb20gPC0gc2V0RXh0ZW50KGp1bmVhdSwgY3JvcCkKIyAKIyBwbG90KGp1bmUuem9vbSwgY29sPWMoYmx1ZS5jb2woNTApLCB0ZXJyYWluLmNvbG9ycyg1MCkpLCBicmVha3M9czMpCmBgYAoKCgoKCiMjIGluIGdncGxvdAoKaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzMyMjcxODIvaG93LXRvLXNldC11c2UtZ2dwbG90Mi10by1tYXAtYS1yYXN0ZXIKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJhc3RlcikKbGlicmFyeShyYXN0ZXJWaXMpCmxpYnJhcnkocmdkYWwpCmxpYnJhcnkoZ3JpZCkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkodmlyaWRpcykgICMgYmV0dGVyIGNvbG9ycyBmb3IgZXZlcnlvbmUKbGlicmFyeShnZ3RoZW1lcykgIyB0aGVtZV9tYXAoKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ25ld3NjYWxlKQpsaWJyYXJ5KHJuYXR1cmFsZWFydGhkYXRhKQpsaWJyYXJ5KHJuYXR1cmFsZWFydGgpCmxpYnJhcnkoZ2dzcGF0aWFsKQoKYGBgCgpgYGB7cn0KZGF0YWZvbGQgPC0gIi4uL2RhdGEvSnVuZWF1QmF0aHkudGlmIgp0ZXN0IDwtIHJhc3RlcihkYXRhZm9sZCkgCmBgYAoKCmBgYHtyfQp0ZXN0X3NwZGYgPC0gYXModGVzdCwgIlNwYXRpYWxQaXhlbHNEYXRhRnJhbWUiKQp0ZXN0X2RmIDwtIGFzLmRhdGEuZnJhbWUodGVzdF9zcGRmKQpjb2xuYW1lcyh0ZXN0X2RmKSA8LSBjKCJ2YWx1ZSIsICJ4IiwgInkiKQoKem9vbS5kZiA8LSB0ZXN0X2RmICU+JQogIGZpbHRlcih4ID4gLTEzNC45NSwgeCA8IC0xMzQuNzYsIHkgPiA1OC40LCB5IDwgNTguNTUpCiAgI2ZpbHRlcih4ID4gLTEzNSwgeCA8IC0xMzQsIHkgPiA1OC4yLCB5IDwgNTguNykgCgp6b29tLmRmLncuZ3JheSA8LSB6b29tLmRmICU+JQogIG11dGF0ZSh2YWx1ZSA9IGlmZWxzZSh2YWx1ZSA+PTAsIE5BLCB2YWx1ZSkpICMgc2V0IGxhbmQgdG8gTkEgdG8gc2hhZGUgaXQgZ3JheQoKIyBmdWxseSB6b29tZWQgb3V0CmZ1bGxfc2l6ZV9yYXN0ZXIgPC0gdGVzdF9kZiAlPiUKICBtdXRhdGUodmFsdWUgPSBpZmVsc2UodmFsdWUgPj0wLCBOQSwgdmFsdWUpKSAjIHNldCBsYW5kIHRvIE5BIHRvIHNoYWRlIGl0IGdyYXkKYGBgCgpgYGB7cn0KIyBtaW5pbXVtIHZhbHVlIGJlbG93IHNlYSBsZXZlbAptaSA8LSBtaW4oem9vbS5kZiR2YWx1ZSkKCiMgQnJlYWsgcG9pbnRzIHNlcXVlbmNlIGZvciBiZWxvdyBzZWEgbGV2ZWwKczEgPC0gc2VxKGZyb209bWksIHRvPTAsIGJ5PTAgLSBtaSAvIDUwKQoKZGVwdGguc2NhbGUgPC0gcm91bmQoczEsIDApCmRlcHRoLnNjYWxlIDwtIHVuaXF1ZShkZXB0aC5zY2FsZSkKCmRlcHRoLnNjYWxlCmJyZWFrcyA9IGxldmVscyhkZXB0aC5zY2FsZSlbZmxvb3Ioc2VxKDEsIG5sZXZlbHMoZGVwdGguc2NhbGUpLCBsZW5ndGgub3V0ID0gMTApKV0KCm5ldy5kZXB0aC5zY2FsZSA8LSBkZXB0aC5zY2FsZVtzZXEoMSwgbGVuZ3RoKGRlcHRoLnNjYWxlKSwgMyldCmBgYAoKCmBgYHtyIGJhdGh5LW1hcC1hbWFsZ2F9Cnpvb21lZC5vdXQgPC0gZ2dwbG90KCkgKyAgCiAgZ2VvbV90aWxlKGRhdGE9em9vbS5kZi53LmdyYXksIGFlcyh4PXgsIHk9eSwgZmlsbD12YWx1ZSksIGFscGhhPTAuOCkgKyAKICBzY2FsZV9maWxsX3N0ZXBzMihsb3cgPSAibmF2eWJsdWUiLAogICAgICAgICAgICAgICAgICAgIG1pZCA9ICJkb2RnZXJibHVlNCIsCiAgaGlnaCA9ICJsaWdodHN0ZWVsYmx1ZTEiLAogIG1pZHBvaW50ID0gLTE1NSwKICBuYS52YWx1ZSA9ICJncmV5NTAiLAogIGJyZWFrcyA9IG5ldy5kZXB0aC5zY2FsZSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odD0xMCkpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyPTEwKSksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKSArIAogICAgICB0aGVtZShsZWdlbmQua2V5LmhlaWdodD11bml0KDEuOCwgImNtIikpICsKICAgICAgbGFicyhmaWxsID0gIkRlcHRoIChtKSIsCiAgICAgICAgIHggPSAiTG9uZ2l0dWRlIChXKSIsCiAgICAgICAgIHkgPSAiTGF0aXR1ZGUgKE4pIikKCnpvb21lZC5vdXQKCiNnZ3NhdmUoInBkZl9vdXRwdXRzL2FtYWxnYUJhdGh5LnBkZiIpCmBgYAoKCgpUaGF0J3MgbW9yZSB6b29tZWQgb3V0LCBidXQgZ2l2ZXMgYSBiZXR0ZXIgcGVyc3BlY3RpdmUuLi4KCgpUaGVuIGhlcmUncyBtb3JlIHpvb21lZCBpbiBvbiBBbWFsZ2E6CgoKYGBge3J9Cm1vcmUuem9vbS5kZiA8LSB0ZXN0X2RmICU+JQogIGZpbHRlcih4ID4gLTEzNC44NSwgeCA8IC0xMzQuNzcsIHkgPiA1OC40MywgeSA8IDU4LjUxKSAlPiUKICBtdXRhdGUodmFsdWUgPSBpZmVsc2UodmFsdWUgPj0wLCBOQSwgdmFsdWUpKSAjIHNldCBsYW5kIHRvIE5BIHRvIHNoYWRlIGl0IGdyYXkKCnpvb21lZC5tYXAgPC0gZ2dwbG90KCkgKyAgCiAgZ2VvbV90aWxlKGRhdGE9bW9yZS56b29tLmRmLCBhZXMoeD14LCB5PXksIGZpbGw9dmFsdWUpLCBhbHBoYT0wLjgpICsgCiAgc2NhbGVfZmlsbF9zdGVwczIobG93ID0gIm5hdnlibHVlIiwKICAgICAgICAgICAgICAgICAgICBtaWQgPSAiZG9kZ2VyYmx1ZTQiLAogIGhpZ2ggPSAibGlnaHRzdGVlbGJsdWUxIiwKICBtaWRwb2ludCA9IC05NywKICAjc3BhY2UgPSAiTGFiIiwKICBuYS52YWx1ZSA9ICJncmV5NTAiLAogIGJyZWFrcyA9IG5ldy5kZXB0aC5zY2FsZSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBtYXJnaW4gPSBtYXJnaW4odD0xMCkpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIG1hcmdpbiA9IG1hcmdpbihyPTEwKSksCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsKICAgICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKSArIAogICAgICB0aGVtZShsZWdlbmQua2V5LmhlaWdodD11bml0KDEuOCwgImNtIikpICsKICAgICAgbGFicyhmaWxsID0gIkRlcHRoIChtKSIsCiAgICAgICAgIHggPSAiTG9uZ2l0dWRlIChXKSIsCiAgICAgICAgIHkgPSAiTGF0aXR1ZGUgKE4pIikKCnpvb21lZC5tYXAKCiNnZ3NhdmUoImFtYWxnYUJhdGh5Wm9vbS5wZGYiKQpgYGAKCkFkZCB0aGUgdHJhbnNlY3QgbGluZSB0byB0aGlzIG1hcD8KCgoKCmBgYHtyfQojIHJlYWQgaW4gdGhlIGRhdGEKIyB0aGlzIGRhdGFmcmFtZSBpcyBwcm9kdWNlZCBpbiB0aGUgQ1REIGFuYWx5c2lzIG9uIHRoZSBWTQojIDA4LWN0ZC1jYXN0LWRhdGEuUm1kCmN0ZCA8LSByZWFkX2NzdigiLi4vZGF0YS9jdGREYXRhZnJhbWUuY3N2IikKCmBgYAoKCmBgYHtyfQojIGp1c3Qgb25lIG9mIHRoZSB0cmFuc2VjdHMgKG5vdCBib3RoIEFNIGFuZCBQTSkKYW0gPC0gY3RkICU+JQogIGZpbHRlcih0aWRlID09ICJBTV9pbmNvbWluZyIpICU+JQogIGRwbHlyOjpzZWxlY3QobGF0LCBsb25nLCBpZCwgZGlzdGFuY2UpICU+JQogIHVuaXF1ZSgpICU+JQogIGZpbHRlcihpZCAhPSAxKQoKYW0gJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgbGFiZWwgPSBpZCkpICsKICBnZW9tX3RleHQoKQpgYGAKCmBgYHtyfQp6b29tZWQubWFwICsKICBnZW9tX3BvaW50KGRhdGEgPSBhbSwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBsYWJlbCA9IGlkKSwgc2l6ZSA9IDEsIGNvbG9yID0gImZpcmVicmljazIiKQoKI2dnc2F2ZSgicGRmX291dHB1dHMvYW1hbGdhVHJhbnNlY3RCYXRoeS5wZGYiLCBoZWlnaHQgPSA2LCB3aWR0aCA9IDcpCmBgYAoKClpvb20gbW9yZQoKYGBge3Igem9vbS1wbG90LWZvci1tYXBwaW5nLWZpZ3VyZXN9CmhpZ2hlc3QucmVzIDwtIG1vcmUuem9vbS5kZiAlPiUKICBmaWx0ZXIoeCA+IC0xMzQuODMsIHggPCAtMTM0Ljc3OSwgeSA+IDU4LjQ3LCB5IDwgNTguNTEpICU+JQogIG11dGF0ZSh2YWx1ZSA9IGlmZWxzZSh2YWx1ZSA+PTAsIE5BLCB2YWx1ZSkpICMgc2V0IGxhbmQgdG8gTkEgdG8gc2hhZGUgaXQgZ3JheQoKaGlnaGVzdC56b29tLnBsb3QgPC0gZ2dwbG90KCkgKyAgCiAgZ2VvbV90aWxlKGRhdGE9aGlnaGVzdC5yZXMsIGFlcyh4PXgsIHk9eSwgZmlsbCA9IHZhbHVlLCBjb2xvciA9IHZhbHVlKSkgKyAKICBzY2FsZV9maWxsX3N0ZXBzMihsb3cgPSAibmF2eWJsdWUiLAogICAgICAgICAgICAgICAgICAgIG1pZCA9ICJkb2RnZXJibHVlNCIsCiAgaGlnaCA9ICJsaWdodHN0ZWVsYmx1ZTEiLAogIG1pZHBvaW50ID0gLTk5LAogIHNwYWNlID0gIkxhYiIsCiAgbmEudmFsdWUgPSAiZ3JleTUwIiwKICBicmVha3MgPSBuZXcuZGVwdGguc2NhbGUpICsKICBzY2FsZV9jb2xvcl9zdGVwczIobG93ID0gIm5hdnlibHVlIiwKICAgICAgICAgICAgICAgICAgICBtaWQgPSAiZG9kZ2VyYmx1ZTQiLAogIGhpZ2ggPSAibGlnaHRzdGVlbGJsdWUxIiwKICBtaWRwb2ludCA9IC05OSwKICBuYS52YWx1ZSA9ICJncmV5NTAiLAogIGJyZWFrcyA9IG5ldy5kZXB0aC5zY2FsZSkgKwogIGNvb3JkX2VxdWFsKCkgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIG1hcmdpbiA9IG1hcmdpbih0PTEwKSksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgbWFyZ2luID0gbWFyZ2luKHI9MTApKSwKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTUwIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsKICAgICAgIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSkgKyAKICAgICAgIyB0aGVtZShsZWdlbmQua2V5LmhlaWdodD11bml0KDEsICJjbSIpKSArCiAgICAgIGxhYnMoZmlsbCA9ICJEZXB0aCAobSkiLAogICAgICAgICB4ID0gIkxvbmdpdHVkZSAoVykiLAogICAgICAgICB5ID0gIkxhdGl0dWRlIChOKSIpICsKICBndWlkZXMoY29sb3IgPSBGKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwwKSwgYnJlYWtzID0gYyg1OC40OCwgNTguNDksIDU4LjUwKSkgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsMCkpCiAgCgpoaWdoZXN0Lnpvb20ucGxvdApgYGAKCgpgYGB7ciBwbG90LWZvci1tYW51c2NyaXB0fQp0cmFuc2VjdDIwMjEgPC0gaGlnaGVzdC56b29tLnBsb3QgKwogIGdlb21fcG9pbnQoZGF0YSA9IGFtLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQpLCBzaXplID0gMSwgY29sb3IgPSAicmVkIikgKwogICNhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuNzk1LCB5ID0gNTguNTA0LCBsYWJlbCA9ICJBbWFsZ2EgSGFyYm9yIiwgc2l6ZSA9IDQpICsKICAjYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0LjgxLCB5ID0gNTguNDk0LCBsYWJlbCA9ICIyMDIxIHRyYW5zZWN0Iiwgc2l6ZSA9IDMsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0Ljc4OTk5LCB5ID0gNTguNDk2LCBsYWJlbCA9ICJwZW5zIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwogICAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0LjgwOCwgeSA9IDU4LjQ5MiwgbGFiZWwgPSAiMTAwMG0iLCBzaXplID0gMywgY29sb3IgPSAiYmxhY2siKSArCiAgICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MjUsIHkgPSA1OC40ODUsIGxhYmVsID0gIjIwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0xMzQuODMsIC0xMzQuNzgpLCBicmVha3MgPSBjKC0xMzQuODIsIC0xMzQuODAsIC0xMzQuNzgpKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYyg1OC40NywgNTguNTEpKSAKICAjbGFicyh0aXRsZSA9ICIyMDIxIHRyYW5zZWN0IikgKwogICNzY2FsZV9maWxsX2NvbnRpbnVvdXMoYnJlYWtzID0gYygwLCAtMTAsIC0yNSwgLTUwLCAtNzUsIC0xMDAsIC0xMjUsIC0xNTAsIC0yMDApKQogIAogIAojZ2dzYXZlKCJwZGZfb3V0cHV0cy9hbWFsZ2FUcmFuc2VjdEJhdGh5Wm9vbS5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDUpCgpgYGAKYXQgNTYwIG0sIGl0J3MgYmFzaWNhbGx5IHRoZSBlZGdlIG9mIEFtYWxnYSBIYXJib3IuCgoKQWRkIHRoZSB0cmFuc2VjdCB0byB0aGUgem9vbWVkIG91dCBtYXA6CmBgYHtyfQptYXAyMDIxX291dCA8LSB6b29tZWQub3V0ICsKICBnZW9tX3BvaW50KGRhdGEgPSBhbSwgYWVzKHggPSBsb25nLCB5ID0gbGF0KSwgc2l6ZSA9IDEsIGNvbG9yID0gImJsYWNrIikgKwogIGFubm90YXRlKGdlb20gPSAidGV4dCIsIHggPSAtMTM0Ljc4NiwgeSA9IDU4LjUxLCBsYWJlbCA9ICJBbWFsZ2EgSGFyYm9yIiwgc2l6ZSA9IDQsIGNvbG9yID0gIndoaXRlIiwgZm9udGZhY2UgPSAiYm9sZCIpICsKICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuODMsIHkgPSA1OC40OTQsIGxhYmVsID0gIjIwMjEgdHJhbnNlY3QiLCBzaXplID0gMywgZm9udGZhY2UgPSAiYm9sZCIpCiAgCgpgYGAKCldpdGggdGhlc2UgbWFwcywgSSBzaG91bGQgYmUgYWJsZSB0byBjb21iaW5lIHRoZSB0d28gdHJhbnNlY3QgeWVhcnMgd2l0aCBjb3dwbG90IG9yIHNpbWlsYXIuCgoKRm9yIHRoZSAyMDIyIGRhdGEsIEkgbmVlZCB0aGUgZXF1aXZhbGVudCBmcm9tIHRoZSBDVEQgY2FzdHMgZm9yIHRoZSB0cmFuc2VjdC4KCkNURCBkYXRhIGZyb20gMjAyMgoKCmBgYHtyIHJlYWQtaW4tZGF0YS1mcm9tLTIwMjJ9CmN0ZC4yMDIyIDwtIHJlYWRfY3N2KCIuLi9kYXRhLzIwMjJjdGREYXRhZnJhbWUuY3N2IikKCmN0ZC4yMDIyCgojIGdyYWIgdGhlIGRhdGEgZm9yIE1heSA1IGFuZCBvbmUgdGlkZSBmb3IgZGF0YSBwb2ludHMgZm9yIHRoZSBtYXAKb25lLnNldC4yMDIyIDwtIGN0ZC4yMDIyICU+JQogIGZpbHRlcihzdHJfZGV0ZWN0KHRpbWUsICIyMDIyLTA1LTA1IikgJgogICAgdGlkZSA9PSAiQU1fb3V0Z29pbmciKSAlPiUKICBkcGx5cjo6c2VsZWN0KGN0ZF9zYW1wbGUsIGxhdCwgbG9uZykgJT4lCiAgdW5pcXVlKCkgJT4lCiAgZmlsdGVyKCFjdGRfc2FtcGxlICVpbiUgYygiMTYzMDI0IiwgIjE5MTkzNSIsICIxNzUzMzciLCAiMTg1MDQ3IikpICU+JQogIG11dGF0ZShZZWFyID0gIjIwMjIiKQoKZnAgPC0gYW0gJT4lCiAgbXV0YXRlKFllYXIgPSAiMjAyMSIpICU+JQogIGJpbmRfcm93cyhvbmUuc2V0LjIwMjIpCgoKYW1hbGdhX3Bsb3QgPC0gaGlnaGVzdC56b29tLnBsb3QgKwogIG5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3BvaW50KGRhdGEgPSBmcCwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBzaGFwZSA9IFllYXIsIGNvbG9yID0gWWVhciksIHNpemUgPSAxLjUpICsKICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuNzg5LCB5ID0gNTguNDk2LCBsYWJlbCA9ICJwZW5zIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwogICAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0LjgwOCwgeSA9IDU4LjQ4NjUsIGxhYmVsID0gIjEwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwogICAgICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuODI2LCB5ID0gNTguNDg1LCBsYWJlbCA9ICIyMDAwbSIsIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiY29yYWwzIiwgImdvbGQiKSkgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE2LDE3KSkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBsZWdlbmQuc3BhY2luZy54ID0gdW5pdCgwLCAiY20iKSwKICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgbGVnZW5kLnNwYWNpbmcgPSB1bml0KDMsICJjbSIpCiAgKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobGFiZWwucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXl3aWR0aCA9IHVuaXQoMC41LCAiY20iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZS5oanVzdCA9IHVuaXQoMC4yLCAiY20iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZS52anVzdCA9IHVuaXQoMC40LCAiY20iKSksCiAgICAgICAgIGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDUpKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKCmFtYWxnYV9wbG90CgpnZ3NhdmUoInBkZl9vdXRwdXRzL2FtYWxnYVRyYW5zZWN0QmF0aHlab29tLnBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNSkKCmBgYAoKYGBge3J9CgojIHoubyA8LSB6b29tZWQub3V0ICsKIyAgIGdlb21fcG9pbnQoZGF0YSA9IG9uZS5zZXQuMjAyMiwgYWVzKHggPSBsb25nLCB5ID0gbGF0KSwgc2l6ZSA9IDEsIGNvbG9yID0gIndoaXRlIikgKwojICAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0LjgsIHkgPSA1OC41MSwgbGFiZWwgPSAiQW1hbGdhIEhhcmJvciIsIHNpemUgPSA0LCBjb2xvciA9ICJibGFjayIpICsKIyAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44NiwgeSA9IDU4LjQ4LCBsYWJlbCA9ICJiYWNrZ3JvdW5kIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuODAsIHkgPSA1OC40NywgbGFiZWwgPSAidHJhbnNlY3RzIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgdGhlbWUoCiMgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKIyAgICkKIyAKIyAgIAojIHouaS4yMiA8LSBoaWdoZXN0Lnpvb20ucGxvdCArCiMgICBnZW9tX3BvaW50KGRhdGEgPSBvbmUuc2V0LjIwMjIsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCksIHNpemUgPSAxLCBjb2xvciA9ICJyZWQiKSArCiMgICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuNzg5OTksIHkgPSA1OC40OTYsIGxhYmVsID0gInBlbnMiLCBzaXplID0gMywgY29sb3IgPSAiYmxhY2siKSArCiMgICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MDgsIHkgPSA1OC40OTIsIGxhYmVsID0gIjEwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MjUsIHkgPSA1OC40ODUsIGxhYmVsID0gIjIwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTEzNC44MywgLTEzNC43OCksIGJyZWFrcyA9IGMoLTEzNC44MiwgLTEzNC44MCwgLTEzNC43OCkpICsKIyAgICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoNTguNDcsIDU4LjUxKSkgKwojICAgdGhlbWUoCiMgICAjICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAojICAgIyAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAojICAgIyAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKIyAgICMgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiMgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSAgCiMgICAgICkKIyAKIyB6LmkuMjEgPC0gaGlnaGVzdC56b29tLnBsb3QgKwojICAgZ2VvbV9wb2ludChkYXRhID0gYW0sIGFlcyh4ID0gbG9uZywgeSA9IGxhdCksIHNpemUgPSAxLCBjb2xvciA9ICJyZWQiKSArCiMgICBhbm5vdGF0ZShnZW9tID0gImxhYmVsIiwgeCA9IC0xMzQuNzg5OTksIHkgPSA1OC40OTYsIGxhYmVsID0gInBlbnMiLCBzaXplID0gMywgY29sb3IgPSAiYmxhY2siKSArCiMgICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MDgsIHkgPSA1OC40OTIsIGxhYmVsID0gIjEwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MjUsIHkgPSA1OC40ODUsIGxhYmVsID0gIjIwMDBtIiwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKwojICAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTEzNC44MywgLTEzNC43OCksIGJyZWFrcyA9IGMoLTEzNC44MiwgLTEzNC44MCwgLTEzNC43OCkpICsKIyAgICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoNTguNDcsIDU4LjUxKSkgKwojICAgIHRoZW1lKAojICAgICAjbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAojICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKIyAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAojICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiMgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKQojICAgKQoKYGBgCgoKCmBgYHtyfQpsaWJyYXJ5KGdnT2NlYW5NYXBzKQpgYGAKYGBge3J9CiMganVuZWF1IDwtIGRhdGEuZnJhbWUobG9uID0gLTEzNC41MTYyLAojICAgICAgICAgICAgICAgICAgICAgIGxhdCA9IDU4LjMzNzE4KQojICAgICAgICAgICAgICAgICAgICAgIAojIGlmIG9ubHkgSSBjb3VsZCBmaWd1cmUgb3V0IGhvdyB0byBhbm5vdGF0ZSBhIGdnb2NlYW5tYXAgICAgICAgICAgICAgICAgICAgCmludGVybV9wIDwtIGJhc2VtYXAobGltaXRzID0gYygtMTM4LCAtMTMyLCA1NiwgNTkpLCBiYXRoeW1ldHJ5ID0gVCwgYmF0aHkuc3R5bGUgPSAicmNiIiwgcm90YXRlID0gVCkgKwogIGxhYnMoeCA9ICIgIiwKICAgICAgIHkgPSAiICIpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IG1hcmdpbihyID0gMC4xLCBsID0gMC4xLCB0ID0gMC41LCBiID0gMC41LCB1bml0ID0gImNtIikKICAgIAogICkKICAKYGBgCgoKYGBge3J9CmFtYWxnYV9wbG90MiA8LSBoaWdoZXN0Lnpvb20ucGxvdCArCiAgI3NjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKC0xMzQuODI1LCAtMTM0Ljc4NSkpICsKICBuZXdfc2NhbGVfY29sb3IoKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZnAsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgc2hhcGUgPSBZZWFyLCBjb2xvciA9IFllYXIpLCBzaXplID0gMS41KSArCiAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0Ljc4OSwgeSA9IDU4LjQ5NiwgbGFiZWwgPSAicGVucyIsIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpICsKICAgIGFubm90YXRlKGdlb20gPSAibGFiZWwiLCB4ID0gLTEzNC44MDgsIHkgPSA1OC40ODY1LCBsYWJlbCA9ICIxMDAwbSIsIHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpICsKICAgICAgYW5ub3RhdGUoZ2VvbSA9ICJsYWJlbCIsIHggPSAtMTM0LjgyNiwgeSA9IDU4LjQ4NSwgbGFiZWwgPSAiMjAwMG0iLCBzaXplID0gMywgY29sb3IgPSAiYmxhY2siKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImNvcmFsMyIsICJnb2xkIikpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygxNiwxNykpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLAogICAgbGVnZW5kLmJveC5zcGFjaW5nID0gdW5pdCgwLjEsICJjbSIpKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDUpKSkgKwogICAgYW5ub3RhdGlvbl9ub3J0aF9hcnJvdyhsb2NhdGlvbiA9ICJibCIsIHdoaWNoX25vcnRoID0gInRydWUiLCAKICAgICAgICBwYWRfeCA9IHVuaXQoMC41LCAiY20iKSwgcGFkX3kgPSB1bml0KDAuOCwgImNtIiksCiAgICAgICAgaGVpZ2h0ID0gdW5pdCgwLjgsICJjbSIpLCB3aWR0aCA9IHVuaXQoMC44LCAiY20iKSwKICAgICAgICBzdHlsZSA9IG5vcnRoX2Fycm93X29yaWVudGVlcmluZyhmaWxsID0gYygiYmxhY2siLCAid2hpdGUiKSkpCgogICMgKSArCiAgIyBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChsYWJlbC5wb3NpdGlvbiA9ICJib3R0b20iLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDEsCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXl3aWR0aCA9IHVuaXQoMC41LCAiY20iKSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLmhqdXN0ID0gdW5pdCgwLjIsICJjbSIpLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUudmp1c3QgPSB1bml0KDAuNCwgImNtIikpKQogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgCgphbWFsZ2FfcGxvdDIKYGBgCgpgYGB7cn0Kd29ybGQgPC0gbmVfY291bnRyaWVzKHNjYWxlID0gIm1lZGl1bSIsIHJldHVybmNsYXNzID0gInNmIikKY2xhc3Mod29ybGQpCgpha19wbG90IDwtIGdncGxvdChkYXRhID0gd29ybGQpICsKICBnZW9tX3NmKCkgKwogIHRoZW1lKHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSAnYWxpY2VibHVlJykpICsKICBjb29yZF9zZih4bGltID0gYygtMTc1LCAtMTI1KSwgeWxpbSA9IGMoNDUsIDY1KSwgZXhwYW5kID0gRikgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKDQ1LDU1LDY1KSkgKwogIGFubm90YXRlKCJyZWN0IiwgeG1pbiA9IC0xMzcsIHhtYXggPSAtMTMyLCB5bWluID0gNTYsIHltYXggPSA1OSwgY29sb3IgPSAicmVkIiwgZmlsbCA9IE5BKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gLTE1NSwgeSA9IDUwLCBsYWJlbCA9ICJQYWNpZmljIE9jZWFuIiwgY29sb3IgPSAiZ3JheTIwIiwgc2l6ZSA9IDMuNSkgKwogICBhbm5vdGF0ZSgidGV4dCIsIHggPSAtMTQ1LCB5ID0gNTcsIGxhYmVsID0gIkd1bGYgb2ZcbkFsYXNrYSIsIGNvbG9yID0gImdyYXkyMCIsIHNpemUgPSAzKSArCiAgdGhlbWUoCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbihyID0gMC4xLCBsID0gMC4xLCB0ID0gMC41LCBiID0gMC41LCB1bml0ID0gImNtIikKICApICsKICBhbm5vdGF0aW9uX3NjYWxlKGxvY2F0aW9uID0gImJsIiwgCiAgICAgICAgICAgICAgICAgIGJhcl9jb2xzID0gYygiZ3JheTUwIiwid2hpdGUiKSwKICAgICAgICAgICAgICAgICAgbGluZV93aWR0aCA9IDAuNCwKICAgICAgICAgICAgICAgICAgaGVpZ2h0ID0gdW5pdCgwLjIsICJjbSIpLAogICAgICAgICAgICAgICAgICBwYWRfeCA9IHVuaXQoMC4yNSwgImNtIiksCiAgICAgICAgICAgICAgICAgIHBhZF95ID0gdW5pdCgwLjI1LCAiY20iKSwKICAgICAgICAgICAgICAgICAgdGV4dF9wYWQgPSB1bml0KDAuMTUsICJjbSIpKSArCiAgICBhbm5vdGF0aW9uX25vcnRoX2Fycm93KGxvY2F0aW9uID0gImJsIiwgd2hpY2hfbm9ydGggPSAidHJ1ZSIsIAogICAgICAgIHBhZF94ID0gdW5pdCgwLjUsICJjbSIpLCBwYWRfeSA9IHVuaXQoMC44LCAiY20iKSwKICAgICAgICBoZWlnaHQgPSB1bml0KDAuNSwgImNtIiksIHdpZHRoID0gdW5pdCgwLjUsICJjbSIpLAogICAgICAgIHN0eWxlID0gbm9ydGhfYXJyb3dfb3JpZW50ZWVyaW5nKGZpbGwgPSBjKCJncmF5NTAiLCAiZ3JheTIwIikpKQoKYWtfcGxvdApgYGAKCgoKQ29tYmluZSB0aGUgYWsgcGxvdCBhbmQgdGhlIEFtYWxnYSBwbG90CmBgYHtyfQoKKGFrX3Bsb3QgfCBpbnRlcm1fcCkgLyBhbWFsZ2FfcGxvdDIgKyBwbG90X2xheW91dChucm93ID0gMiwgaGVpZ2h0cyA9IGMoMiwzKSkgKwogIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gJ0EnKSAmCiAgdGhlbWUocGxvdC50YWcgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDAuMSwgMC4xLCAwLjEsIDAuMSwgImNtIikpCgpnZ3NhdmUoInBkZl9vdXRwdXRzL2FtYWxnYV9jb21iaW5lZF9tYXBzLnBuZyIsIHdpZHRoID0gNywgaGVpZ2h0ID0gNykKCmBgYAoKCgoKCgoKCiMjIE5PVCBJTiBVU0UgQkVMT1cgVEhJUyBMSU5FCgoKCiMjIEJhdGh5IHRyYW5zZWN0CgpgYGB7cn0KbGlicmFyeShtYXJtYXApCmBgYAoKCmBgYHtyfQojIGxvYWQgZGF0YXNldHMKIyAJZGF0YShudy5hdGxhbnRpYyk7IGFzLmJhdGh5KG53LmF0bGFudGljKSAtPiBhdGwKIyAJZGF0YShudy5hdGxhbnRpYy5jb2FzdCkKIyAKIyAjIEV4YW1wbGUgMS4gZ2V0LnRyYW5zZWN0KCksIHdpdGhvdXQgdXNlIG9mIGxvY2F0b3IoKQojIAlnZXQudHJhbnNlY3QoYXRsLCAtNjUsIDQzLC01OSw0MCkgLT4gdGVzdCA7IHBsb3QodGVzdFssM11+dGVzdFssMl0sdHlwZT0ibCIpCiMgCWdldC50cmFuc2VjdChhdGwsIC02NSwgNDMsLTU5LDQwLCBkaXN0YW5jZT1UUlVFKSAtPiB0ZXN0IDsgcGxvdCh0ZXN0Wyw0XX50ZXN0WywzXSx0eXBlPSJsIikKIyAKIyAjIEV4YW1wbGUgMi4gZ2V0LnRyYW5zZWN0KCksIHdpdGhvdXQgdXNlIG9mIGxvY2F0b3IoKTsgcHJldHR5IHBsb3QKIyAJcGFyKG1mcm93PWMoMiwxKSxtYWk9YygxLjIsIDEsIDAuMSwgMC4xKSkKIyAJcGxvdChhdGwsIGRlZXA9LTYwMDAsIHNoYWxsb3c9LTEwLCBzdGVwPTEwMDAsIGx3ZD0wLjUsIGNvbD0iZ3JleTUwIixkcmF3bGFiZWxzPVRSVUUpCiMgCWxpbmVzKG53LmF0bGFudGljLmNvYXN0KQojIAojIAlnZXQudHJhbnNlY3QoYXRsLCAtNzUsIDQ0LC00NiwzMiwgbG9jPUZBTFNFLCBkaXM9VFJVRSkgLT4gdGVzdAojIAlwb2ludHModGVzdCRsb24sdGVzdCRsYXQsdHlwZT0ibCIsY29sPSJibHVlIixsd2Q9MixsdHk9MikKIyAJcGxvdFByb2ZpbGUodGVzdCkKIyAKIyAjIEV4YW1wbGUgMy4gZ2V0LnRyYW5zZWN0KCksIHdpdGggdXNlIG9mIGxvY2F0b3IoKTsgcHJldHR5IHBsb3QKIyAjIyBOb3QgcnVuOiAKIyAJcGFyKG1mcm93PWMoMiwxKSxtYWk9YygxLjIsIDEsIDAuMSwgMC4xKSkKIyAJcGxvdChhdGwsIGRlZXA9LTYwMDAsIHNoYWxsb3c9LTEwLCBzdGVwPTEwMDAsIGx3ZD0wLjUsIGNvbD0iZ3JleTUwIixkcmF3bGFiZWxzPVRSVUUpCiMgCWxpbmVzKG53LmF0bGFudGljLmNvYXN0KQojIAkKIyAJZ2V0LnRyYW5zZWN0KGF0bCwgbG9jPVRSVUUsIGRpcz1UUlVFLCBjb2w9MiwgbHR5PTIpIC0+IHRlc3QKIyAJcGxvdFByb2ZpbGUodGVzdCkKCQojIyBFbmQoTm90IHJ1bikKYGBgCgoKVmVyeSBjb29sLCBzbyBpZiBJIGNhbiBnZXQgbXkgZGF0YSBpbnRvIHRoYXQgZm9ybWF0LCBJIHNob3VsZCBiZSBhYmxlIHRvIGRvIHNvbWV0aGluZyBzaW1pbGFyLgoKYGBge3J9CiMgLTEzNC43OTI1LCAtMTM0LjgyMTMsIDU4LjQ5NDYsIDU4LjQ4NDgKIyBsb2FkIGRhdGFzZXQKanVuZWF1IDwtIGdldE5PQUEuYmF0aHkobG9uMSA9IC0xNDAsIGxvbjIgPSAtMTMwLApsYXQxID0gNjAsIGxhdDIgPSA1NSwgcmVzb2x1dGlvbiA9IDEpCgpwbG90KGp1bmVhdSkKc3VtbWFyeShqdW5lYXUpCgpgYGAKCgpgYGB7cn0KIyBDcmVhdGluZyBhIGN1c3RvbSBwYWxldHRlIG9mIGJsdWVzCmJsdWVzIDwtIGMoImxpZ2h0c3RlZWxibHVlNCIsICJsaWdodHN0ZWVsYmx1ZTMiLAoibGlnaHRzdGVlbGJsdWUyIiwgImxpZ2h0c3RlZWxibHVlMSIpCiMgUGxvdHRpbmcgdGhlIGJhdGh5bWV0cnkgd2l0aCBkaWZmZXJlbnQgY29sb3JzIGZvciBsYW5kIGFuZCBzZWEKcGxvdChqdW5lYXUsIGltYWdlID0gVFJVRSwgbGFuZCA9IFRSVUUsIGx3ZCA9IDAuMSwKYnBhbCA9IGxpc3QoYygwLCBtYXgoanVuZWF1KSwgImdyZXkiKSwKYyhtaW4oanVuZWF1KSwwLGJsdWVzKSkpCiMgTWFraW5nIHRoZSBjb2FzdGxpbmUgbW9yZSB2aXNpYmxlCnBsb3QoanVuZWF1LCBkZWVwID0gMCwgc2hhbGxvdyA9IDAsIHN0ZXAgPSAwLApsd2QgPSAwLjQsIGFkZCA9IFRSVUUpCmBgYAoKem9vbSBvdXQgZXZlbiBtb3JlIHRvIGdpdmUgcGVyc3BlY3RpdmUgb24gbG9jYXRpb246CgpgYGB7cn0KanVuZWF1Lm91dCA8LSBnZXROT0FBLmJhdGh5KGxvbjEgPSAtMTc5LCBsb24yID0gLTEyNSwKbGF0MSA9IDc1LCBsYXQyID0gNDgsIHJlc29sdXRpb24gPSA4KQoKcGxvdChqdW5lYXUub3V0LCBpbWFnZSA9IFRSVUUsIGxhbmQgPSBUUlVFLCBsd2QgPSAwLjEsCmJwYWwgPSBsaXN0KGMoMCwgbWF4KGp1bmVhdS5vdXQpLCAiZ3JleSIpLApjKG1pbihqdW5lYXUub3V0KSwwLGJsdWVzKSkpCiMgTWFraW5nIHRoZSBjb2FzdGxpbmUgbW9yZSB2aXNpYmxlCnBsb3QoanVuZWF1Lm91dCwgZGVlcCA9IDAsIHNoYWxsb3cgPSAwLCBzdGVwID0gMCwKbHdkID0gMC40LCBhZGQgPSBUUlVFKQpgYGAKCgoKClRoYXQgZG9lc24ndCBsb29rIGFzIGdvb2QgYXMgdGhlIERFTSwgYnV0IGhvcGVmdWxseSBpdCBjYW4gZ2l2ZSB1cyB0aGUgYmF0aHkgdHJhbnNlY3QuCgpgYGB7cn0KIyBFeGFtcGxlIDEuIGdldC50cmFuc2VjdCgpLCB3aXRob3V0IHVzZSBvZiBsb2NhdG9yKCkKCWdldC50cmFuc2VjdChqdW5lYXUsIC0xMzQuODIxMywgNTguNDg0OCwgLTEzNC43OTI1LCA1OC40OTQ2KSAtPiB0ZXN0IDsgcGxvdCh0ZXN0WywzXX50ZXN0WywyXSx0eXBlPSJsIikKCWdldC50cmFuc2VjdChqdW5lYXUsIC0xMzQuODIxMywgNTguNDg0OCwgLTEzNC43OTI1LCA1OC40OTQ2LCBkaXN0YW5jZT1UUlVFKSAtPiB0ZXN0IDsgcGxvdCh0ZXN0Wyw0XX50ZXN0WywzXSx0eXBlPSJsIikKCiMgRXhhbXBsZSAyLiBnZXQudHJhbnNlY3QoKSwgd2l0aG91dCB1c2Ugb2YgbG9jYXRvcigpOyBwcmV0dHkgcGxvdAoJcGFyKG1mcm93PWMoMiwxKSxtYWk9YygxLjIsIDEsIDAuMSwgMC4xKSkKCXBsb3QoanVuZWF1LCBkZWVwPS04MDAsIHNoYWxsb3c9LTEwLCBzdGVwPTEwMDAsIGx3ZD0wLjUsIGNvbD0iZ3JleTUwIixkcmF3bGFiZWxzPVRSVUUpCgkjbGluZXMobncuYXRsYW50aWMuY29hc3QpCgoJZ2V0LnRyYW5zZWN0KGp1bmVhdSwgLTEzNC44MjEzLCA1OC40ODQ4LCAtMTM0Ljc5MjUsIDU4LjQ5NDYsIGxvYz1GQUxTRSwgZGlzPVRSVUUpIC0+IHRlc3QKCXBvaW50cyh0ZXN0JGxvbix0ZXN0JGxhdCx0eXBlPSJsIixjb2w9ImJsdWUiLGx3ZD0yLGx0eT0yKQoJcGxvdFByb2ZpbGUodGVzdCkKCiMgRXhhbXBsZSAzLiBnZXQudHJhbnNlY3QoKSwgd2l0aCB1c2Ugb2YgbG9jYXRvcigpOyBwcmV0dHkgcGxvdAojIyBOb3QgcnVuOiAKCXBhcihtZnJvdz1jKDIsMSksbWFpPWMoMS4yLCAxLCAwLjEsIDAuMSkpCglwbG90KGp1bmVhdSwgZGVlcD0tODAwLCBzaGFsbG93PS0xMCwgc3RlcD0xMDAwLCBsd2Q9MC41LCBjb2w9ImdyZXk1MCIsZHJhd2xhYmVscz1UUlVFKQoJbGluZXMoanVuZWF1KQoKCWdldC50cmFuc2VjdChqdW5lYXUsIGxvYz1UUlVFLCBkaXM9VFJVRSwgY29sPTIsIGx0eT0yKSAtPiB0ZXN0CglwbG90UHJvZmlsZSh0ZXN0KQpgYGAKYGBge3J9CmJhdGh5LmRmIDwtIGhpZ2hlc3QucmVzICU+JQogIHJlbmFtZShsb25naXR1ZGUgPSB4LCBsYXRpdHVkZSA9IHksIGRlcHRoID0gdmFsdWUpCgpiYXRoeS5kZiAlPiUKICB3cml0ZV9jc3YoImFtYWxnYUJhdGh5RGYuY3N2IikKCnJlYWQuYmF0aHkoImFtYWxnYUJhdGh5REYuY3N2IiwgaGVhZGVyID0gVCkKYGBgCgoKYGBge3J9CiMgRXhhbXBsZSAxLiBnZXQudHJhbnNlY3QoKSwgd2l0aG91dCB1c2Ugb2YgbG9jYXRvcigpCglnZXQudHJhbnNlY3QoaGlnaGVzdC5yZXMsIC0xMzQuODIxMywgNTguNDg0OCwgLTEzNC43OTI1LCA1OC40OTQ2KSAtPiB0ZXN0IDsgcGxvdCh0ZXN0WywzXX50ZXN0WywyXSx0eXBlPSJsIikKCWdldC50cmFuc2VjdChoaWdoZXN0LnJlcywgLTEzNC44MjEzLCA1OC40ODQ4LCAtMTM0Ljc5MjUsIDU4LjQ5NDYsIGRpc3RhbmNlPVRSVUUpIC0+IHRlc3QgOyBwbG90KHRlc3RbLDRdfnRlc3RbLDNdLHR5cGU9ImwiKQoKIyBFeGFtcGxlIDIuIGdldC50cmFuc2VjdCgpLCB3aXRob3V0IHVzZSBvZiBsb2NhdG9yKCk7IHByZXR0eSBwbG90CglwYXIobWZyb3c9YygyLDEpLG1haT1jKDEuMiwgMSwgMC4xLCAwLjEpKQoJcGxvdCh0ZXN0X3NwZGYsIGRlZXA9LTgwMCwgc2hhbGxvdz0tMTAsIHN0ZXA9MTAwMCwgbHdkPTAuNSwgY29sPSJncmV5NTAiLGRyYXdsYWJlbHM9VFJVRSkKCSNsaW5lcyhudy5hdGxhbnRpYy5jb2FzdCkKCglnZXQudHJhbnNlY3QodGVzdF9zcGRmLCAtMTM0LjgyMTMsIDU4LjQ4NDgsIC0xMzQuNzkyNSwgNTguNDk0NiwgbG9jPUZBTFNFLCBkaXM9VFJVRSkgLT4gdGVzdAoJcG9pbnRzKHRlc3QkbG9uLHRlc3QkbGF0LHR5cGU9ImwiLGNvbD0iYmx1ZSIsbHdkPTIsbHR5PTIpCglwbG90UHJvZmlsZSh0ZXN0KQoKIyBFeGFtcGxlIDMuIGdldC50cmFuc2VjdCgpLCB3aXRoIHVzZSBvZiBsb2NhdG9yKCk7IHByZXR0eSBwbG90CiMjIE5vdCBydW46IAoJcGFyKG1mcm93PWMoMiwxKSxtYWk9YygxLjIsIDEsIDAuMSwgMC4xKSkKCXBsb3QodGVzdF9zcGRmLCBkZWVwPS04MDAsIHNoYWxsb3c9LTEwLCBzdGVwPTEwMDAsIGx3ZD0wLjUsIGNvbD0iZ3JleTUwIixkcmF3bGFiZWxzPVRSVUUpCglsaW5lcyh0ZXN0X3NwZGYpCgpgYGAKCgoKCgoKQWxzbyB0aGlzOgpodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy80NzA0NzYyMy9wcm9qZWN0cmFzdGVyLXJhc3Rlci1wcm9qZWN0aW9uLW9mLWJhdGh5bWV0cnktZGF0YS1ub2FhLW5jLWluLXRoZS1wYWNpZmljCgo=